package ValiEngine

import (
	"Ahq/Models"
	"fmt"
	"strconv"
	"strings"
)

func Vali(str string, rules ...CalcRule) bool {
	// fmt.Println(str)
	// fmt.Printf("Vali:%#v\n", rules)
	express := strings.ReplaceAll(str, " ", "")
	if len(express) == 0 {
		return false
	}
	if len(rules) == 0 {
		return true
	}

	postfixExpress, errRet := transPostfixExpress(express)
	if errRet != nil {
		fmt.Println(errRet)
		return false
	}

	// fmt.Println(postfixExpress)

	//	后缀表达式求值
	result, errRet := calc(postfixExpress, rules)
	if errRet != nil {
		fmt.Println("error:", errRet)
		return false
	}
	fmt.Println("result::", result)
	return true
}

func priority(s byte) int {
	switch s {
	case Models.Addition:
		return 1
	case Models.Subtraction:
		return 1
	case Models.Multiplication:
		return 2
	case Models.Division:
		return 2
	}
	return 0
}

//	将中缀表达式转换成后缀表达式（逆波兰式），postfixExpress：后缀表达式
func transPostfixExpress(express string) (postfixExpress []string, err error) {
	var (
		opStack Stack //	运算符堆栈
		i       int
	)

LABEL:
	for i < len(express) { //	从左至右扫描中缀表达式
		switch {
		//	1. 若读取的是操作数，则将该操作数存入后缀表达式。
		case express[i] >= '0' && express[i] <= '9':
			var number []byte //	如数字123，由'1'、'2'、'3'组成
			for ; i < len(express); i++ {
				if express[i] < '0' || express[i] > '9' {
					break
				}
				number = append(number, express[i])
			}
			postfixExpress = append(postfixExpress, string(number))

		//	2. 若读取的是运算符：
		//	(1) 该运算符为左括号"("，则直接压入运算符堆栈。
		case express[i] == '(':
			opStack.Push(fmt.Sprintf("%c", express[i]))
			i++

		//	(2) 该运算符为右括号")"，则输出运算符堆栈中的运算符到后缀表达式，直到遇到左括号为止。
		case express[i] == ')':
			for !opStack.IsEmpty() {
				data, _ := opStack.Pop()
				if data[0] == '(' {
					break
				}
				postfixExpress = append(postfixExpress, data)
			}
			i++

		//	(3) 该运算符为非括号运算符:
		case express[i] == Models.Addition || express[i] == Models.Subtraction || express[i] == Models.Multiplication || express[i] == Models.Division:
			//	(a)若运算符堆栈为空,则直接压入运算符堆栈。
			if opStack.IsEmpty() {
				opStack.Push(fmt.Sprintf("%c", express[i]))
				i++
				continue LABEL
			}

			data, _ := opStack.Top()
			//	(b)若运算符堆栈栈顶的运算符为括号，则直接压入运算符堆栈。(只可能为左括号这种情况)
			if data[0] == '(' {
				opStack.Push(fmt.Sprintf("%c", express[i]))
				i++
				continue LABEL
			}
			//	(c)若比运算符堆栈栈顶的运算符优先级低或相等，则输出栈顶运算符到后缀表达式,直到栈为空或者找到优先级高于当前运算符。并将当前运算符压入运算符堆栈。
			if priority(express[i]) <= priority(data[0]) {
				tmp := priority(express[i])
				for !opStack.IsEmpty() && tmp <= priority(data[0]) {
					postfixExpress = append(postfixExpress, data)
					opStack.Pop()
					data, _ = opStack.Top()
				}
				opStack.Push(fmt.Sprintf("%c", express[i]))
				i++
				continue LABEL
			}
			//	(d)若比运算符堆栈栈顶的运算符优先级高，则直接压入运算符堆栈。
			opStack.Push(fmt.Sprintf("%c", express[i]))
			i++

		default:
			err = fmt.Errorf("invalid express:%v,%v,%s,%s,%s", express[i], i, express[0], express[1])
			return
		}
	}

	//	3. 扫描结束，将运算符堆栈中的运算符依次弹出，存入后缀表达式。
	for !opStack.IsEmpty() {
		data, _ := opStack.Pop()
		if data[0] == '#' {
			break
		}
		postfixExpress = append(postfixExpress, data)
	}

	return
}

//	后缀表达式求值
func calc(postfixExpress []string, rules []CalcRule) (result int64, err error) {
	var (
		num1 string
		num2 string
		s    Stack //	操作栈，用于存入操作数，运算符
	)

	//	从左至右扫描后缀表达式
	for i := 0; i < len(postfixExpress); i++ {
		var cur = postfixExpress[i]

		//	1. 若读取的是运算符
		if cur[0] == Models.Addition || cur[0] == Models.Subtraction || cur[0] == Models.Multiplication || cur[0] == Models.Division {
			//	从操作栈中弹出两个数进行运算
			num1, err = s.Pop()
			if err != nil {
				return
			}
			num2, err = s.Pop()
			if err != nil {
				return
			}

			//	先弹出的数为B，后弹出的数为A
			B, _ := strconv.Atoi(num1)
			A, _ := strconv.Atoi(num2)
			for _, r := range rules {
				// fmt.Printf("plugin:%#v\n", r)
				if !r.Vali(cur[0], A, B) {
					err = fmt.Errorf(fmt.Sprint("Validation rule ", r.GetKey(), " failed"))
					return
				}
			}
			var res int

			switch cur[0] {
			case Models.Addition:
				res = A + B
			case Models.Subtraction:
				res = A - B
			case Models.Multiplication:
				res = A * B
			case Models.Division:
				res = A / B
			default:
				err = fmt.Errorf("invalid operation")
				return
			}

			//	将中间结果压栈
			s.Push(fmt.Sprintf("%d", res))
			//fmt.Println("mid value = ", res)
		} else {
			//	1. 若读取的是操作数，直接压栈
			s.Push(cur)
		}
	}

	//	计算结束，栈顶保存最后结果
	resultStr, err := s.Top()
	if err != nil {
		return
	}
	result, err = strconv.ParseInt(resultStr, 10, 64)
	return
}
